数据移动是DPC++图的另一个重点，它对于理解应用程序性能至关重要。数据移动在程序中隐式地发生(使用缓冲区和访问器或使用USM共享分配)，常常会忽略。接下来，我们将研究数据移动在DPC++中影响图执行的不同方式。\par

\hspace*{\fill} \par %插入空行
\textbf{显式}

显式数据移动的优点是会显式地出现在图中，使开发者可以清楚地看到图中发生了什么。我们把显式数据操作分为用于USM的和用于缓冲区的。\par

如在第6章，当需要在设备内存和主机内存间复制数据时，USM中的显式数据移动就会发生，可以通过memcpy完成。提交操作或命令组将返回一个事件，该事件可用于与其他命令组对操作进行排序。\par

通过调用命令组处理程序对象的copy或update\_host，将发生缓冲区的显式数据移动。复制方法可用于在主机内存和设备上的访问器对象之间交换数据，一个简单的例子是检查一个长时间运行的计算序列。使用copy的话，数据可以以单向方式从设备写入到主机内存中。如果是使用缓冲区完成的，大多数情况下(例如，不是以use\_host\_ptr创建的)将要求数据首先复制到主机，然后从缓冲区的内存到所需的主机内存。\par

update\_host是一种非常特殊的复制形式。如果基于主机内存创建了缓冲区，则此方法将把访问器表示的数据复制回原始主机内存。如果程序手动将主机数据与use\_mutex属性创建的缓冲区同步，那么这个操作将非常有用。但是，这种用例不太可能出现在大多数的程序中。\par

\hspace*{\fill} \par %插入空行
\textbf{隐式}

隐式数据移动可能对DPC++中的命令组和任务图产生隐藏的效果。通过隐式数据移动，数据可以通过DPC++运行时或某种硬件和软件的组合在主机和设备之间复制。这两种情况下，复制不需要显式进行。让我们再次分别看看USM和缓冲区的例子。\par

对于USM，隐式数据移动发生在主机和共享内存中。主机内存并没有真正移动数据，而是远程访问数据，共享内存可能在主机和设备之间迁移。由于这种迁移是自动进行的，所以对于USM隐式数据移动和命令组实际上没有什么需要考虑的。然而，共享内存有一些微妙之处值得了解。\par

预取操作以类似于memcpy的方式工作，以便让运行时在内核尝试使用共享分配之前开始迁移。然而，与memcpy不同，数据必须复制才能确保结果正确，预取通常视为运行时提高性能的提示，预取不会使内存中的指针值失效。如果预取在内核开始执行之前没有完成，程序仍会正确执行，因为预取并不是一个功能需求，所以很多代码可能会选择使图中的命令组不依赖于预取操作。\par

缓冲区也有一些细微差别。使用缓冲区时，命令组必须为指定如何使用数据的缓冲区构造访问器。这些数据依赖关系表示了不同命令组之间的顺序，并允许构建任务图。然而，带有缓冲区的命令组有时会进行另外的操作:指定数据移动。\par

访问器指定内核将读取或写入缓冲区。由此得出的结论是，设备上的数据必须可用，如果不可用，运行时必须在内核开始执行之前将其移到设备上。因此，DPC++运行时必须跟踪当前缓冲区位置，以便安排数据移动操作。访问器在图中创建了一个隐藏节点。如果数据移动是必需的，运行时必须先执行。这样，提交的内核才能正常执行。\par

再看看图8-8。这个例子中，前两个内核将需要将缓冲区data1和data2复制到设备中，运行时隐式地创建图节点来执行数据移动。当提交第三个内核的命令组时，这些缓冲区可能仍然在设备上，因此运行时将不需要执行数据移动。第四个内核的数据也可能不需要数据移动，但是主机访问器的创建需要运行时在访问器可用之前将缓冲区data1移动回主机。\par



























































